<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Java并发体系-第四阶段-AQS源码解读-[1] | 风祈的时光录</title><meta name="keywords" content="Java并发，AQS源码"><meta name="author" content="youthlql"><meta name="copyright" content="youthlql"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="万字系列长文讲解-Java并发体系-第四阶段-AQS源码解读-[1]。">
<meta property="og:type" content="article">
<meta property="og:title" content="Java并发体系-第四阶段-AQS源码解读-[1]">
<meta property="og:url" content="https://imlql.cn/post/92c4503d.html">
<meta property="og:site_name" content="风祈的时光录">
<meta property="og:description" content="万字系列长文讲解-Java并发体系-第四阶段-AQS源码解读-[1]。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png">
<meta property="article:published_time" content="2020-10-26T09:59:42.000Z">
<meta property="article:modified_time" content="2022-07-24T11:02:09.800Z">
<meta property="article:author" content="youthlql">
<meta property="article:tag" content="Java并发">
<meta property="article:tag" content="AQS源码">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png"><link rel="shortcut icon" href="https://npm.elemecdn.com/youthlql@1.0.8/blog/favicon.png"><link rel="canonical" href="https://imlql.cn/post/92c4503d"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://npm.elemecdn.com/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?f693ff99cc7e613b88cf5b729a14b48b";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Java并发体系-第四阶段-AQS源码解读-[1]',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-07-24 19:02:09'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/ali_icon.css"><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/2-24-mogai.css"><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="风祈的时光录" type="application/atom+xml">
</head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/avatar/2.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><div class="data-item"><a href="/archives/"><div class="headline">文章</div><div class="length-num">37</div></a></div><div class="data-item"><a href="/tags/"><div class="headline">标签</div><div class="length-num">21</div></a></div><div class="data-item"><a href="/categories/"><div class="headline">分类</div><div class="length-num">13</div></a></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">风祈的时光录</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">Java并发体系-第四阶段-AQS源码解读-[1]</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2020-10-26T09:59:42.000Z" title="发表于 2020-10-26 17:59:42">2020-10-26</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-07-24T11:02:09.800Z" title="更新于 2022-07-24 19:02:09">2022-07-24</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java%E5%B9%B6%E5%8F%91/">Java并发</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java%E5%B9%B6%E5%8F%91/%E5%8E%9F%E7%90%86/">原理</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">8.9k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>33分钟</span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="可重入锁"><a href="#可重入锁" class="headerlink" title="可重入锁"></a>可重入锁</h1><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: youthlql-吕</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/10/22 21:12</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 可重入锁:</span></span><br><span class="line"><span class="comment"> * 1、可重复可递归调用的锁，在外层使用锁之后，在内层仍然可以使用，并且不发生死锁，这样的锁就叫做可重入锁。</span></span><br><span class="line"><span class="comment"> * 2、是指在同一个线程在外层方法获取锁的时候，再进入该线程的内层方法会自动获取锁(前提，锁对象得是同一个</span></span><br><span class="line"><span class="comment"> * 对象)，不会因为之前已经获取过还没释放而阻塞</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ReEnterLockDemo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> Object objectLockA = <span class="keyword">new</span> Object();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">m1</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (objectLockA)&#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------外层调用&quot;</span>);</span><br><span class="line">                <span class="keyword">synchronized</span> (objectLockA)&#123;</span><br><span class="line">                    System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------中层调用&quot;</span>);</span><br><span class="line">                    <span class="keyword">synchronized</span> (objectLockA)</span><br><span class="line">                    &#123;</span><br><span class="line">                        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------内层调用&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        m1();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ReEnterLockDemo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">m1</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;=====外层&quot;</span>);</span><br><span class="line">        m2();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">m2</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;=====中层&quot;</span>);</span><br><span class="line">        m3();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">m3</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;=====内层&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> ReEnterLockDemo().m1();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br></pre></td></tr></table></figure>





<h1 id="LockSupport"><a href="#LockSupport" class="headerlink" title="LockSupport"></a>LockSupport</h1><h2 id="是什么？"><a href="#是什么？" class="headerlink" title="是什么？"></a>是什么？</h2><blockquote>
<p>官方说明：<a target="_blank" rel="noopener" href="https://www.apiref.com/java11-zh/java.base/java/util/concurrent/locks/LockSupport.html">https://www.apiref.com/java11-zh/java.base/java/util/concurrent/locks/LockSupport.html</a></p>
</blockquote>
<p>LockSupport中的park()和unpark()的作用分别是阻塞线程和解除阻塞线程，相当于线程等待和唤醒机制的加强版。</p>
<h2 id="3种让线程等待和唤醒的方法"><a href="#3种让线程等待和唤醒的方法" class="headerlink" title="3种让线程等待和唤醒的方法"></a>3种让线程等待和唤醒的方法</h2><ul>
<li>方式1:  使用Object中的wait()方法让线程等待， 使用Object中的notify()方法唤醒线程</li>
<li>方式2:  使用JUC包中Condition的await()方法让线程等待，使用signal()方法唤醒线程 </li>
<li>方式3:  LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程</li>
</ul>
<h2 id="Object类提供的等待唤醒机制的缺点"><a href="#Object类提供的等待唤醒机制的缺点" class="headerlink" title="Object类提供的等待唤醒机制的缺点"></a>Object类提供的等待唤醒机制的缺点</h2><h3 id="正常情况下"><a href="#正常情况下" class="headerlink" title="正常情况下"></a>正常情况下</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LockSupportDemo1</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> Object objectLock = <span class="keyword">new</span> Object();</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (objectLock)&#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------come in&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    objectLock.wait();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------被唤醒&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (objectLock)</span><br><span class="line">            &#123;</span><br><span class="line">                objectLock.notify();</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------通知&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;B&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结果：</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">A	------come in</span><br><span class="line">B	------通知</span><br><span class="line">A	------被唤醒</span><br><span class="line"></span><br><span class="line">Process finished with exit code 0</span><br></pre></td></tr></table></figure>



<h3 id="异常情况1"><a href="#异常情况1" class="headerlink" title="异常情况1"></a>异常情况1</h3><p>去掉同步代码块</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LockSupportDemo1</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> Object objectLock = <span class="keyword">new</span> Object();</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line"><span class="comment">//            synchronized (objectLock)&#123;</span></span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------come in&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    objectLock.wait();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------被唤醒&quot;</span>);</span><br><span class="line"><span class="comment">//            &#125;</span></span><br><span class="line">        &#125;,<span class="string">&quot;A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line"><span class="comment">//            synchronized (objectLock)&#123;</span></span><br><span class="line">                objectLock.notify();</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------通知&quot;</span>);</span><br><span class="line"><span class="comment">//            &#125;</span></span><br><span class="line">        &#125;,<span class="string">&quot;B&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;    </span><br></pre></td></tr></table></figure>

<p>结果：</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">A	------come in</span><br><span class="line">Exception in thread &quot;A&quot; Exception in thread &quot;B&quot; java.lang.IllegalMonitorStateException</span><br><span class="line">	at java.lang.Object.wait(Native Method)</span><br><span class="line">	at java.lang.Object.wait(Object.java:502)</span><br><span class="line">	at com.youth.guiguthirdquarter.AQS.LockSupportDemo1.lambda$main$0(LockSupportDemo1.java:16)</span><br><span class="line">	at java.lang.Thread.run(Thread.java:748)</span><br><span class="line">java.lang.IllegalMonitorStateException</span><br><span class="line">	at java.lang.Object.notify(Native Method)</span><br><span class="line">	at com.youth.guiguthirdquarter.AQS.LockSupportDemo1.lambda$main$1(LockSupportDemo1.java:26)</span><br><span class="line">	at java.lang.Thread.run(Thread.java:748)</span><br><span class="line"></span><br><span class="line">Process finished with exit code 0</span><br></pre></td></tr></table></figure>

<p>报错了。</p>
<h3 id="异常情况2"><a href="#异常情况2" class="headerlink" title="异常情况2"></a>异常情况2</h3><p>先唤醒，再等待。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LockSupportDemo1</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> Object objectLock = <span class="keyword">new</span> Object();</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">2</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">synchronized</span> (objectLock)&#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------come in&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    objectLock.wait();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------被唤醒&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (objectLock)</span><br><span class="line">            &#123;</span><br><span class="line">                objectLock.notify();</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;\t&quot;</span>+<span class="string">&quot;------通知&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;B&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;    </span><br></pre></td></tr></table></figure>

<p>结果：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">B	------通知</span><br><span class="line">A	------come in</span><br><span class="line"></span><br><span class="line">Process finished with exit code -<span class="number">1</span></span><br></pre></td></tr></table></figure>

<p>死循环，A无法被唤醒了。</p>
<p>这两点我们之前也说过，Object类提供的wait和notify</p>
<p>1、只能在synchronized同步代码块里使用</p>
<p>2、只能先等待（wait），再唤醒（notify）。顺序一旦错了，那个等待线程就无法被唤醒了。</p>
<h2 id="Condion类提供的等待唤醒机制的缺点"><a href="#Condion类提供的等待唤醒机制的缺点" class="headerlink" title="Condion类提供的等待唤醒机制的缺点"></a>Condion类提供的等待唤醒机制的缺点</h2><p>缺点和Object类里的wait，notify一样。</p>
<p>1、只能在lock同步代码块里使用，不然就报错</p>
<p>2、只能先等待（await），再唤醒（signal）。顺序一旦错了，那个等待线程就无法被唤醒了。</p>
<p>但相对于wait，notify改进的一点是，可以绑定lock进行定向唤醒。</p>
<h2 id="LockSupport的优点"><a href="#LockSupport的优点" class="headerlink" title="LockSupport的优点"></a>LockSupport的优点</h2><p>有的时候我不需要进入同步代码块，我只是需要让线程阻塞，这个时候LockSupport就发挥作用了。并且还解决了之前的第二个问题，也就是等待必须在唤醒的前面。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">park</span><span class="params">()</span>  <span class="comment">//除非许可证可用，否则禁用当前线程以进行线程调度。 </span></span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span>	<span class="title">unpark</span><span class="params">(Thread thread)</span>	<span class="comment">//如果给定线程尚不可用，则为其提供许可。</span></span></span><br></pre></td></tr></table></figure>

<ul>
<li><p>LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。</p>
</li>
<li><p>LockSupport类使用了一种名为Permit(许可）的概念来做到阻塞和唤醒线程的功能，每个线程都有一个许可(permit),<br>permit只有两个值1和零，默认是零。可以把许可看成是一种(0,1)信号量(Semaphore），但与Semaphore不同的是，许可的累加上限是1。</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">park</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        UNSAFE.park(<span class="keyword">false</span>, <span class="number">0L</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>LockSupport底层还是UNSAFE（前面讲过）。</p>
<ul>
<li><p>permit默认是0，所以一开始调用park()方法，当前线程就会阻塞，直到别的线程将当前线程的permit设置为1时,park方法会被唤醒，然后会将permit再次设置为0并返回。</p>
</li>
<li><p>调用unpark(thread)方法后，就会将thread线程的许可permit设置成1(注意多次调用unpark方法，不会累加，permit值还是1)会自动唤醒thread线程，即之前阻塞中的LockSupport.park()方法会立即返回。</p>
</li>
<li><p>LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1，0的开关，默认是0，<br>调用一次unpark就将0变成1，<br>调用一次park会消费permit，也就是将1变成o，同时park立即返回。<br>如再次调用park会变成阻塞(因为permit为零了会阻塞在这里，一直到permit变为1)，这时调用unpark会把permit置为1。<br>每个线程都有一个相关的permit, permit最多只有一个，重复调用unpark也不会积累凭证。</p>
</li>
<li><p>形象的理解<br>线程阻塞需要消耗凭证(permit)，这个凭证最多只有1个。<br>当调用park方法时</p>
<pre><code>如果有凭证，则会直接消耗掉这个凭证然后正常退出;
如果无凭证，就必须阻塞等待凭证可用;
</code></pre>
<p>而unpark则相反，它会增加一个凭证，但凭证最多只能有1个，累加无效。</p>
</li>
</ul>
<p>我们用LockSupport来测试下之前的异常场景</p>
<h3 id="异常情况1-1"><a href="#异常情况1-1" class="headerlink" title="异常情况1"></a>异常情况1</h3><p>无同步代码块</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LockSupportDemo3</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         LockSupport：俗称 锁中断</span></span><br><span class="line"><span class="comment">         LockSupport它的解决的痛点</span></span><br><span class="line"><span class="comment">         1。LockSupport不用持有锁块，不用加锁，程序性能好，</span></span><br><span class="line"><span class="comment">         2。不需要等待和唤醒的先后顺序，不容易导致卡死</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line"></span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t ----begin-时间：&quot;</span> + System.currentTimeMillis());</span><br><span class="line">            LockSupport.park();<span class="comment">//阻塞当前线程</span></span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t ----被唤醒-时间：&quot;</span> + System.currentTimeMillis());</span><br><span class="line">        &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">        t1.start();</span><br><span class="line">        LockSupport.unpark(t1);</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 通知t1...&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结果：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">t1	 ----begin-时间：<span class="number">1603376148147</span></span><br><span class="line">t1	 ----被唤醒-时间：<span class="number">1603376148147</span></span><br><span class="line">main	 通知t1...</span><br><span class="line"></span><br><span class="line">Process finished with exit code <span class="number">0</span></span><br></pre></td></tr></table></figure>

<p>没有问题</p>
<h3 id="异常情况2-1"><a href="#异常情况2-1" class="headerlink" title="异常情况2"></a>异常情况2</h3><p>先唤醒，再阻塞（等待）。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">2</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t ----begin-时间：&quot;</span> + System.currentTimeMillis());</span><br><span class="line">            LockSupport.park();<span class="comment">//阻塞当前线程</span></span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t ----被唤醒-时间：&quot;</span> + System.currentTimeMillis());</span><br><span class="line">        &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">        t1.start();</span><br><span class="line">        LockSupport.unpark(t1);</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 通知t1...&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>结果：</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">main	 通知t1...</span><br><span class="line">t1	 ----begin-时间：1603376257183</span><br><span class="line">t1	 ----被唤醒-时间：1603376257183</span><br><span class="line"></span><br><span class="line">Process finished with exit code 0</span><br></pre></td></tr></table></figure>

<p>可以看到，如果你先唤醒了。那么后面的<code>LockSupport.park();</code>就相当于瞬间被唤醒了，不会和之前一样程序卡死。为什么呢？结合之前分析的流程</p>
<p>1、先执行unpark，将许可证由0变为1</p>
<p>2、然后park来了发现许可证此时为0（也就是有许可证），那么他就不会阻塞，马上就往后执行。同时消耗许可证（也就是将1又变为0）。</p>
<h1 id="AQS"><a href="#AQS" class="headerlink" title="AQS"></a>AQS</h1><h2 id="AQS是什么？"><a href="#AQS是什么？" class="headerlink" title="AQS是什么？"></a>AQS是什么？</h2><p><strong>字面意思：</strong>抽象的队列同步器</p>
<p><strong>技术翻译：</strong>是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石， 通过内置的FIFO队列来完成资源获取线程的排队工作，并通过一个int类变量<code>state</code>表示持有锁的状态。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0001.png">



<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0011.png">

<p>AbstractOwnableSynchronizer<br>AbstractQueuedLongSynchronizer<br>AbstractQueuedSynchronizer    </p>
<p>上面几个都是AQS，但是通常地: AbstractQueuedSynchronizer简称为AQS。</p>
<p>AQS是一个抽象的父类，可以将其理解为一个框架。基于AQS这个框架，我们可以实现多种同步器，比如下方图中的几个Java内置的同步器。同时我们也可以基于AQS框架实现我们自己的同步器以满足不同的业务场景需求。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0002.png">



<h2 id="AQS能干嘛？"><a href="#AQS能干嘛？" class="headerlink" title="AQS能干嘛？"></a>AQS能干嘛？</h2><p>加锁会导致阻塞：有阻塞就需要排队，实现排队必然需要有某种形式的队列来进行管理</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0003.png">

<p>1、抢到资源的线程直接使用办理业务，抢占不到资源的线程的必然涉及一种<strong>排队等候机制</strong>，抢占资源失败的线程继续去等待(类似办理窗口都满了，暂时没有受理窗口的顾客只能去候客区排队等候)，仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号，轮到了再去受理窗口办理业务）。</p>
<p>2、既然说到了<strong>排队等候机制</strong>，那么就一定 会有某种队列形成，这样的队列是什么数据结构呢?</p>
<p>3、如果共享资源被占用，就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的，将暂时获取不到锁的线程加入到队列中，这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的结点(Node) ，通过CAS、自旋以及LockSuport.park()的方式，维护state变量的状态，使并发达到同步的效果。</p>
<h1 id="AQS独占模式（以ReentrantLock-源码为例）"><a href="#AQS独占模式（以ReentrantLock-源码为例）" class="headerlink" title="AQS独占模式（以ReentrantLock 源码为例）"></a>AQS独占模式（以ReentrantLock 源码为例）</h1><h2 id="AQS结构"><a href="#AQS结构" class="headerlink" title="AQS结构"></a>AQS结构</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 头结点，你直接把它当做当前持有锁的线程 可能是最好理解的。实际上可能略有出入，往下看分析即可</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Node head;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 阻塞的尾节点，每个新的节点进来，都插入到最后，也就形成了一个链表</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Node tail;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个是最重要的，代表当前锁的状态，0代表没有被占用，大于 0 代表有线程持有当前锁</span></span><br><span class="line"><span class="comment">// 这个值可以大于 1，是因为锁可以重入，每次重入都加上 1</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">int</span> state;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 代表当前持有独占锁的线程，举个最重要的使用例子，因为锁可以重入</span></span><br><span class="line"><span class="comment">// reentrantLock.lock()可以嵌套调用多次，所以每次用这个来判断当前线程是否已经拥有了锁</span></span><br><span class="line"><span class="comment">// if (currentThread == getExclusiveOwnerThread()) &#123;state++&#125;</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> Thread exclusiveOwnerThread; <span class="comment">//继承自AbstractOwnableSynchronizer</span></span><br></pre></td></tr></table></figure>



<h2 id="Node类结构"><a href="#Node类结构" class="headerlink" title="Node类结构"></a>Node类结构</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 标识节点当前在共享模式下</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> Node SHARED = <span class="keyword">new</span> Node();</span><br><span class="line">    <span class="comment">// 标识节点当前在独占模式下</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> Node EXCLUSIVE = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ======== 下面的几个int常量是给waitStatus用的 ===========</span></span><br><span class="line">    <span class="comment">/** waitStatus value to indicate thread has cancelled */</span></span><br><span class="line">    <span class="comment">// 代码此线程取消了争抢这个锁</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> CANCELLED =  <span class="number">1</span>;</span><br><span class="line">    <span class="comment">/** waitStatus value to indicate successor&#x27;s thread needs unparking */</span></span><br><span class="line">    <span class="comment">// 官方的描述是，其表示当前node的后继节点对应的线程需要被唤醒</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SIGNAL    = -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">/** waitStatus value to indicate thread is waiting on condition */</span></span><br><span class="line">    <span class="comment">// 等待condition唤醒</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> CONDITION = -<span class="number">2</span>;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * waitStatus value to indicate the next acquireShared should</span></span><br><span class="line"><span class="comment">     * unconditionally propagate</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="comment">// 共享模式同步状态获取讲会无条件的传播下去（共享模式下，该字段才会使用）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PROPAGATE = -<span class="number">3</span>;</span><br><span class="line">    <span class="comment">// ===============-2和-3用的不多，暂时不分析======================================</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 取值为上面的1、-1、-2、-3，或者0(以后会讲到，waitStatus初始值为0)</span></span><br><span class="line">    <span class="comment">// 这么理解，暂时只需要知道如果这个值 大于0 代表此线程取消了等待，</span></span><br><span class="line">    <span class="comment">//    ps: 半天抢不到锁，不抢了，ReentrantLock是可以指定timeouot的</span></span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">int</span> waitStatus;</span><br><span class="line">    <span class="comment">// 前驱节点的引用</span></span><br><span class="line">    <span class="keyword">volatile</span> Node prev;</span><br><span class="line">    <span class="comment">// 后继节点的引用</span></span><br><span class="line">    <span class="keyword">volatile</span> Node next;</span><br><span class="line">    <span class="comment">// 这个就是线程本尊</span></span><br><span class="line">    <span class="keyword">volatile</span> Thread thread;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Node 的数据结构其实也挺简单的，就是 thread + waitStatus + pre + next 四个属性而已，大家先要有这个概念在心里。</p>
<h2 id="AQS队列基本结构"><a href="#AQS队列基本结构" class="headerlink" title="AQS队列基本结构"></a>AQS队列基本结构</h2><img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0004.png">

<p>注意排队队列，不包括head（也就是后文要说的哨兵节点）。</p>
<h2 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> com.youth.guiguthirdquarter.AQS;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.ReentrantLock;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: youthlql-吕</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2020/10/25 21:59</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 功能描述:</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AQSDemo</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//带入一个银行办理业务的案例来模拟我们的AQS如何进行线程的管理和通知唤醒机制</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//3个线程模拟3个来银行网点，受理窗口办理业务的顾客</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//A顾客就是第一个顾客，此时受理窗口没有任何人，A可以直接去办理</span></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;-----A thread come in&quot;</span>);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123; TimeUnit.MINUTES.sleep(<span class="number">20</span>); &#125;<span class="keyword">catch</span> (Exception e) &#123;e.printStackTrace();&#125;</span><br><span class="line">            &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//第二个顾客，第二个线程---》由于受理业务的窗口只有一个(只能一个线程持有锁)，此时B只能等待，</span></span><br><span class="line">        <span class="comment">//进入候客区</span></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;-----B thread come in&quot;</span>);</span><br><span class="line">            &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;B&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//第三个顾客，第三个线程---》由于受理业务的窗口只有一个(只能一个线程持有锁)，此时C只能等待，</span></span><br><span class="line">        <span class="comment">//进入候客区</span></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;-----C thread come in&quot;</span>);</span><br><span class="line">            &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,<span class="string">&quot;C&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以这样的一个实际例子说明。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0005.png">



<h2 id="非公平锁lock-加锁"><a href="#非公平锁lock-加锁" class="headerlink" title="非公平锁lock()加锁"></a>非公平锁lock()加锁</h2><h3 id="lock"><a href="#lock" class="headerlink" title="lock()"></a>lock()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">NonfairSync</span> <span class="keyword">extends</span> <span class="title">Sync</span> </span>&#123;</span><br><span class="line">       <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">7316153563782823691L</span>;</span><br><span class="line"></span><br><span class="line">       <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">           <span class="comment">/*</span></span><br><span class="line"><span class="comment">           1、非公平锁不公平的第一个原因就出现在这里。刚准备加锁的线程，这里会用CAS抢一下锁（也就是通过</span></span><br><span class="line"><span class="comment">           看state的状态）。如果抢成功了就调用setExclusiveOwnerThread，设置当前持有独占锁的线程为本</span></span><br><span class="line"><span class="comment">           线程。</span></span><br><span class="line"><span class="comment">           */</span></span><br><span class="line">           <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, <span class="number">1</span>))</span><br><span class="line">               setExclusiveOwnerThread(Thread.currentThread());</span><br><span class="line">           <span class="keyword">else</span></span><br><span class="line">               <span class="comment">//如果抢锁失败就走入这个流程，抢锁失败说明当前锁已经被占用了</span></span><br><span class="line">               acquire(<span class="number">1</span>);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">return</span> nonfairTryAcquire(acquires);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line"><span class="comment">//相当于只要调用了这个方法，说明线程独占锁成功</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">setExclusiveOwnerThread</span><span class="params">(Thread thread)</span> </span>&#123;</span><br><span class="line">       exclusiveOwnerThread = thread;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>A线程刚进来的时候，AQS的head和tail节点都还没有被初始化，则会被默认初始化为null。并且state默认初始化为0。</p>
</blockquote>
<p>1、A线程进去窗口办理业务，此时state == 0，那么CAS就直接成功了，并且把sate改为1。然后调用下<code>setExclusiveOwnerThread</code>，就直接结束了。【加锁成功，直接返回】</p>
<p><strong>B线程</strong></p>
<p>1、接着B线程去窗口办理业务，因为之前A线程把state变为了1，那么B线程在进行第一个if-CAS判断就会失败。所以就走到了else分支，调用<code>acquire(1)</code>方法。</p>
<p><strong>C线程</strong></p>
<p>因为A线程占用着锁，C线程执行逻辑和B一样。（后续假设C进行加锁时间在B后面一点）</p>
<h3 id="acquire-和tryAcquire"><a href="#acquire-和tryAcquire" class="headerlink" title="acquire()和tryAcquire()"></a>acquire()和tryAcquire()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、acquire()方法来自父类AQS，我们看到，这个方法，如果tryAcquire(arg) 返回true, 也就结束了。</span></span><br><span class="line"><span class="comment">   否则，acquireQueued方法会将线程压到队列中。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123; <span class="comment">// 此时 arg == 1</span></span><br><span class="line">       </span><br><span class="line">       <span class="comment">/*</span></span><br><span class="line"><span class="comment">       1、首先调用tryAcquire(1)一下，名字上就知道，这个只是试一试。因为有可能直接就成功了呢，也就不需要</span></span><br><span class="line"><span class="comment">       进队列排队了。</span></span><br><span class="line"><span class="comment">       2、有可能成功的情况就是，在走到这一步的时候，前面占锁的线程刚好释放锁</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">       <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">           <span class="comment">// tryAcquire(arg)没有成功，这个时候需要把当前线程挂起，放到阻塞队列中。</span></span><br><span class="line">           acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) &#123;</span><br><span class="line">             selfInterrupt();</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、上面的tryAcquire里会直接调用ReentrantLock类的nonfairTryAcquire方法，</span></span><br><span class="line"><span class="comment">2、尝试直接获取锁，返回值是boolean，代表是否获取到锁</span></span><br><span class="line"><span class="comment">   3、有两种情况会返回true：</span></span><br><span class="line"><span class="comment">   	1.没有线程在等待锁</span></span><br><span class="line"><span class="comment">   	2.重入锁，线程本来就持有锁，也就可以理所当然可以直接获取</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">nonfairTryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">final</span> Thread current = Thread.currentThread();</span><br><span class="line">           <span class="keyword">int</span> c = getState();</span><br><span class="line">       	<span class="comment">/*</span></span><br><span class="line"><span class="comment">       	1、state == 0 此时此刻没有线程持有锁</span></span><br><span class="line"><span class="comment">       	2、前面也说了有可能成功的情况就是，在走到这一步的时候，前面占锁的线程刚好释放锁</span></span><br><span class="line"><span class="comment">       	*/</span></span><br><span class="line">           <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">               <span class="comment">//那就用CAS尝试一下，成功了就获取到锁了。</span></span><br><span class="line">               <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">                   setExclusiveOwnerThread(current);</span><br><span class="line">                   <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line">       	<span class="comment">// 会进入这个else if分支，说明是重入锁了，需要操作：state=state+1</span></span><br><span class="line">           <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">               <span class="keyword">int</span> nextc = c + acquires;</span><br><span class="line">               <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">                   <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">               setState(nextc);</span><br><span class="line">               <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>B线程</strong></p>
<p>1、B线程最终走进了<code>nonfairTryAcquire()</code>方法，但是因为A还在占锁（占着处理窗口state），所以此时state为1，B线程走到else if分支进行判断。</p>
<p>2、B线程发现已经占有锁的线程不是自己，说明不是重入锁，也不会进入else if分支。最终返回fasle，回到<code>tryAcquire</code>，准备挂起线程。</p>
<p><strong>C线程</strong></p>
<p>因为A线程占用着锁，C线程执行逻辑和B一样</p>
<h3 id="addWaiter"><a href="#addWaiter" class="headerlink" title="addWaiter()"></a>addWaiter()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   </span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、假设tryAcquire(arg) 返回false，那么代码将执行：acquireQueued(addWaiter(Node.EXCLUSIVE),</span></span><br><span class="line"><span class="comment">arg)，这个方法，首先需要执行：addWaiter(Node.EXCLUSIVE)</span></span><br><span class="line"><span class="comment">2、此方法的作用是把线程包装成node，同时进入到队列中。参数mode此时是Node.EXCLUSIVE，代表独占模式</span></span><br><span class="line"><span class="comment">3、以下几行代码想把当前node加到链表的最后面去，也就是进到队列的最后</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> Node <span class="title">addWaiter</span><span class="params">(Node mode)</span> </span>&#123;</span><br><span class="line">       Node node = <span class="keyword">new</span> Node(Thread.currentThread(), mode);</span><br><span class="line">       <span class="comment">//得到尾节点（head和tail在没有初始化前都是null，没有初始化的时候也说明队列为空）</span></span><br><span class="line">       Node pred = tail;</span><br><span class="line"></span><br><span class="line">       <span class="comment">//队列不为空时（即之前已经初始化过了），会进入下面这个分支，此时只需要将新的node加入队尾</span></span><br><span class="line">       <span class="keyword">if</span> (pred != <span class="keyword">null</span>) &#123; </span><br><span class="line">           <span class="comment">// 将当前的队尾节点，设置为自己的前驱 </span></span><br><span class="line">           node.prev = pred; </span><br><span class="line">           <span class="comment">// 用CAS把自己设置为队尾, 如果成功后，tail == node 了，这个节点成为排队队列新的尾巴</span></span><br><span class="line">           <span class="keyword">if</span> (compareAndSetTail(pred, node)) &#123; </span><br><span class="line">               <span class="comment">/*</span></span><br><span class="line"><span class="comment">               1、进到这里说明设置成功，当前node==tail, 将自己与之前的队尾相连，上面已经有</span></span><br><span class="line"><span class="comment">               node.prev = pred，加上下面这句，也就实现了和之前的尾节点双向连接了</span></span><br><span class="line"><span class="comment">               */</span></span><br><span class="line">               pred.next = node;</span><br><span class="line">               <span class="comment">// 线程入队了，可以返回了</span></span><br><span class="line">               <span class="keyword">return</span> node;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">       </span><br><span class="line">       <span class="comment">/*</span></span><br><span class="line"><span class="comment">       1、仔细看看上面的代码，有两种情况会走到这里</span></span><br><span class="line"><span class="comment">          1、pred==null(说明队列是空的) </span></span><br><span class="line"><span class="comment">          2、CAS设置队尾失败(有线程在竞争入队)</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">       enq(node);</span><br><span class="line">       <span class="keyword">return</span> node;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<blockquote>
<p>之前说了A线程刚进来的时候，AQS的head和tail节点都还没有被初始化，则会被默认初始化为null</p>
</blockquote>
<p><strong>B线程</strong></p>
<p>1、B线程进入<code>addWaiter()</code>，发现pred == null，直接进入<code>enq()</code></p>
<p><strong>C线程</strong></p>
<p>1、【前面说了C在B后面】，C线程进来后和B不一样，因为B在后面已经设置了tail指针。那么C线程在判断的时候pred 就不是null，就直接进入了if分支</p>
<p>2、C在if逻辑里准备入队，进行相应设置后，变成下面这样。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0006.png">



<h3 id="enq"><a href="#enq" class="headerlink" title="enq()"></a>enq()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、采用空的for循环，以自旋的方式入队，到这个方法只有两种可能：队列为空，或者有线程竞争入队【上面说过】</span></span><br><span class="line"><span class="comment">   2、自旋在这边的语义是：CAS设置tail过程中，竞争一次竞争不到，我就多次竞争，总会排到的</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> Node <span class="title">enq</span><span class="params">(<span class="keyword">final</span> Node node)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">           Node t = tail;</span><br><span class="line">           </span><br><span class="line">          	<span class="comment">/*</span></span><br><span class="line"><span class="comment">          	1、进入这个分支，说明是队列为空的这种情况，那么就准备初始化一个空的节点（new Node()）</span></span><br><span class="line"><span class="comment">          	作为排队队列的head。</span></span><br><span class="line"><span class="comment">          	*/</span></span><br><span class="line">           <span class="keyword">if</span> (t == <span class="keyword">null</span>) &#123; <span class="comment">// Must initialize</span></span><br><span class="line">               <span class="comment">/*</span></span><br><span class="line"><span class="comment">               1、初始化head节点，前面说过 head 和 tail 初始化的时候都是 null 的。</span></span><br><span class="line"><span class="comment">               2、还是一步CAS，因为可能是很多线程同时进来呢   </span></span><br><span class="line"><span class="comment">               */</span></span><br><span class="line">               <span class="keyword">if</span> (compareAndSetHead(<span class="keyword">new</span> Node()))                    </span><br><span class="line">              <span class="comment">/*</span></span><br><span class="line"><span class="comment">               1、注意这里传的参数是new Node()，说明是一个空的节点（并不是我们B线程封装的节点，</span></span><br><span class="line"><span class="comment">               这个空节点只作为占位符，称作傀儡节点或者哨兵节点）。这个时候head节点的waitStatus==0,</span></span><br><span class="line"><span class="comment">               看new Node()构造方法就知道了。注意：new Node()虽然是空节点，但他不是null</span></span><br><span class="line"><span class="comment">               2、这个时候有了head，但是tail还是null，设置一下，把tail指向head，放心，马上就有</span></span><br><span class="line"><span class="comment">               线程要来了，到时候tail就要被抢了</span></span><br><span class="line"><span class="comment">               3、注意：这里只是设置了tail=head，这里可没return哦。所以，设置完了以后，继续for</span></span><br><span class="line"><span class="comment">               循环，下次就到下面的else分支了</span></span><br><span class="line"><span class="comment">               */</span></span><br><span class="line">                   tail = head;</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;             </span><br><span class="line">               <span class="comment">/*</span></span><br><span class="line"><span class="comment">               1、下面几行，和上一个方法 addWaiter 是一样的，只是这个套在无限循环里，就是将当前</span></span><br><span class="line"><span class="comment">               线程排到队尾，有线程竞争的话排不上重复排，直到排上了再return </span></span><br><span class="line"><span class="comment">               【这里看不懂的话就看下面的例子】               </span></span><br><span class="line"><span class="comment">               */</span></span><br><span class="line">               </span><br><span class="line">               node.prev = t;</span><br><span class="line">               <span class="keyword">if</span> (compareAndSetTail(t, node)) &#123;</span><br><span class="line">                   t.next = node;</span><br><span class="line">                   <span class="keyword">return</span> t;</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>B线程</strong></p>
<p><strong>第一轮循环</strong></p>
<p>1、B线程进入enq()。首先发现t == tail 依然为null，那么就直接进入if分支。</p>
<p>2、进入if分支后，调用<code>compareAndSetHead(new Node())</code>准备初始化head节点。注意这里传的参数是<code>new Node()</code>，说明是一个空的节点（并不是我们B线程封装的节点，这个空节点只作为占位符，<strong>称作傀儡节点或者哨兵节点</strong>），然后将head赋值给tail。</p>
<blockquote>
<p> 补充：双向链表中，第一个节点为虚节点(也叫哨兵节点)，其实并不存储任何信息，只是占位。 真正的第一个有数据的节点，是从第二个节点开始的。</p>
</blockquote>
<p>此时队列变成了下面的样子：</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0007.png">

<p>3、然后if结束之后，继续空的for循环，B线程开始了第二轮循环。</p>
<p><strong>第二轮循环</strong></p>
<p>1、第二次循环再过来的时候，t == tail，但此时tail不再为null，所以进入else分支。</p>
<p>2、<code>node.prev = t</code>，进入if之后，让B节点的prev指针指向t，然后<code>compareAndSetTail(t, node)</code>设置尾节点</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0008.png">

<p>3、CAS设置尾节点成功之后，执行if里的逻辑</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0009.png">





<h3 id="acquireQueued"><a href="#acquireQueued" class="headerlink" title="acquireQueued()"></a>acquireQueued()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、现在，又回到这段代码了</span></span><br><span class="line"><span class="comment">    if (!tryAcquire(arg) </span></span><br><span class="line"><span class="comment">           &amp;&amp; acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) </span></span><br><span class="line"><span class="comment">        selfInterrupt();</span></span><br><span class="line"><span class="comment">   2、acquireQueued这个方法，参数node，经过addWaiter(Node.EXCLUSIVE)，此时已经进入排队队列队尾</span></span><br><span class="line"><span class="comment">   3、注意一下：如果acquireQueued(addWaiter(Node.EXCLUSIVE), arg))返回true的话，意味着上面这段</span></span><br><span class="line"><span class="comment">   代码将进入selfInterrupt()</span></span><br><span class="line"><span class="comment">   4、这个方法非常重要，真正的线程挂起，然后被唤醒后去获取锁，都在这个方法里了</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">   <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">acquireQueued</span><span class="params">(<span class="keyword">final</span> Node node, <span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">boolean</span> failed = <span class="keyword">true</span>;</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">           <span class="keyword">boolean</span> interrupted = <span class="keyword">false</span>;</span><br><span class="line">           <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">               <span class="keyword">final</span> Node p = node.predecessor();             </span><br><span class="line">               <span class="comment">/*</span></span><br><span class="line"><span class="comment">               1、p == head 说明当前节点虽然进到了排队队列，但是是队列的第一个，因为它的前驱是head</span></span><br><span class="line"><span class="comment">               （或者说是哨兵节点，因为head指向了哨兵节点）</span></span><br><span class="line"><span class="comment">               2、注意，队列不包含head节点，head一般指的是占有锁的线程，head后面的才称为排队队列</span></span><br><span class="line"><span class="comment">               3、所以当前节点可以去试抢一下锁</span></span><br><span class="line"><span class="comment">               4、这里我们说一下，为什么可以去试试：它是排队队列队头，所以作为队头，可以去试一试能不能</span></span><br><span class="line"><span class="comment">               拿到锁，因为可能之前的线程已经释放锁了。如果尝试成功，那它就不需要被挂起，直接拿锁，</span></span><br><span class="line"><span class="comment">               效率会高</span></span><br><span class="line"><span class="comment">               5、tryAcquire已经分析过了, 忘记了请往前看一下，就是简单用CAS试操作一下state</span></span><br><span class="line"><span class="comment">               */</span></span><br><span class="line">               <span class="keyword">if</span> (p == head &amp;&amp; tryAcquire(arg)) &#123;</span><br><span class="line">                   setHead(node);</span><br><span class="line">                   p.next = <span class="keyword">null</span>; <span class="comment">// help GC，这个后面释放锁的时候会讲</span></span><br><span class="line">                   failed = <span class="keyword">false</span>;</span><br><span class="line">                   <span class="keyword">return</span> interrupted;</span><br><span class="line">               &#125;</span><br><span class="line">               </span><br><span class="line">               <span class="comment">/*</span></span><br><span class="line"><span class="comment">               1、到这里，说明上面的if分支没有成功。</span></span><br><span class="line"><span class="comment">                 1、要么当前node本来就不是队头，</span></span><br><span class="line"><span class="comment">                 2、要么就是tryAcquire(arg)没有抢赢别人，继续往下看</span></span><br><span class="line"><span class="comment">               */</span></span><br><span class="line">               <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                   parkAndCheckInterrupt())</span><br><span class="line">                   interrupted = <span class="keyword">true</span>;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">           <span class="comment">//tryAcquire()方法抛异常时，failed为true，会取消当前节点的排队。</span></span><br><span class="line">           <span class="keyword">if</span> (failed)</span><br><span class="line">               cancelAcquire(node);<span class="comment">//取消排队</span></span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p><strong>B线程</strong></p>
<p>1、进入<code>acquireQueued()</code>后，发现也是一个空循环。首先通过<code>node.predecessor()</code>得到B节点的前一个节点P，也就是哨兵节点。</p>
<p>2、p == head为true。然后if里再次执行<code>tryAcquire(arg)</code>拿一次锁【流程前面已经分析过了，不重复了】。因为A线程任然持有锁，所以最终结果B节点<code>tryAcquire</code>失败。准备挂起线程</p>
<h3 id="shouldParkAfterFailedAcquire"><a href="#shouldParkAfterFailedAcquire" class="headerlink" title="shouldParkAfterFailedAcquire()"></a>shouldParkAfterFailedAcquire()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、会到这里就是没有抢到锁呗，这个方法说的是：&quot;当前线程没有抢到锁，是否需要挂起当前线程？&quot;</span></span><br><span class="line"><span class="comment">   第一个参数是前驱节点，第二个参数才是代表当前线程的节点</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">shouldParkAfterFailedAcquire</span><span class="params">(Node pred, Node node)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">int</span> ws = pred.waitStatus;</span><br><span class="line">       <span class="comment">// 前驱节点的 waitStatus == -1 ，说明前驱节点状态正常，当前线程需要挂起，直接可以返回true</span></span><br><span class="line">       <span class="keyword">if</span> (ws == Node.SIGNAL)</span><br><span class="line">           <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">       <span class="comment">/*</span></span><br><span class="line"><span class="comment">       1、前驱节点 waitStatus大于0 ，之前说过，大于0说明前驱节点取消了排队。</span></span><br><span class="line"><span class="comment">       2、这里需要知道这点：进入阻塞队列排队的线程会被挂起，而唤醒的操作是由前驱节点完成的。所以</span></span><br><span class="line"><span class="comment">       下面这块代码说的是将当前节点的prev指向waitStatus&lt;=0的节点，简单说，就是为了找个好爹，因为你还</span></span><br><span class="line"><span class="comment">       得依赖它来唤醒呢，如果前驱节点取消了排队，找前驱节点的前驱节点做爹，往前遍历总能找到一个好爹的。</span></span><br><span class="line"><span class="comment">       */</span></span><br><span class="line">       <span class="keyword">if</span> (ws &gt; <span class="number">0</span>) &#123;</span><br><span class="line">           <span class="comment">/*</span></span><br><span class="line"><span class="comment">            * Predecessor was cancelled. Skip over predecessors and</span></span><br><span class="line"><span class="comment">            * indicate retry.</span></span><br><span class="line"><span class="comment">            */</span></span><br><span class="line">           <span class="keyword">do</span> &#123;</span><br><span class="line">               node.prev = pred = pred.prev;</span><br><span class="line">           &#125; <span class="keyword">while</span> (pred.waitStatus &gt; <span class="number">0</span>);</span><br><span class="line">           pred.next = node;</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="comment">/*</span></span><br><span class="line"><span class="comment">            * waitStatus must be 0 or PROPAGATE.  Indicate that we</span></span><br><span class="line"><span class="comment">            * need a signal, but don&#x27;t park yet.  Caller will need to</span></span><br><span class="line"><span class="comment">            * retry to make sure it cannot acquire before parking.</span></span><br><span class="line"><span class="comment">            */</span></span><br><span class="line">           <span class="comment">/*</span></span><br><span class="line"><span class="comment">           1、如果进入到这个分支意味着什么，前驱节点的waitStatus不等于-1和1，那也就是只可能是0，-2，-3</span></span><br><span class="line"><span class="comment">           在我们前面的源码中，都没有看到有设置waitStatus的，所以每个新的node入队时，waitStatu都是0</span></span><br><span class="line"><span class="comment">           2、正常情况下，前驱节点是之前的 tail，那么它的 waitStatus 应该是 0，用CAS将前驱节点</span></span><br><span class="line"><span class="comment">           的waitStatus设置为Node.SIGNAL(也就是-1)，表示我后面有节点需要被唤醒。</span></span><br><span class="line"><span class="comment">           3、这里可以简单说下 waitStatus 中 SIGNAL(-1) 状态的意思，Doug Lea 注释的是：代表后继</span></span><br><span class="line"><span class="comment">           节点需要被唤醒。也就是说这个 waitStatus 其实代表的不是自己的状态，而是后继节点的状态，</span></span><br><span class="line"><span class="comment">           我们知道，每个node 在入队的时候，都会把前驱节点的状态改为 SIGNAL，然后阻塞，等待被前驱唤醒。</span></span><br><span class="line"><span class="comment">           这里涉及的是两个问 题：有线程取消了排队、唤醒操作。其实本质是一样的，读者也可以顺着 </span></span><br><span class="line"><span class="comment">           “waitStatus代表后继节点的状态”这种思路去看一遍源码。</span></span><br><span class="line"><span class="comment">           */</span></span><br><span class="line">           compareAndSetWaitStatus(pred, ws, Node.SIGNAL);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="comment">// 这个方法返回 false，那么会再走一次 for 循序，然后再次进来此方法，此时会从第一个分支返回 true</span></span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   </span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)</span></span><br><span class="line"><span class="comment">   这个方法结束根据返回值我们简单分析下：</span></span><br><span class="line"><span class="comment">      1、如果返回true, 说明前驱节点的waitStatus==-1，是正常情况，那么当前线程需要被挂起，等待以后被唤醒</span></span><br><span class="line"><span class="comment">      我们也说过，以后是被前驱节点唤醒，就等着前驱节点拿到锁，然后释放锁的时候叫你好了</span></span><br><span class="line"><span class="comment">      2、如果返回false, 说明当前不需要被挂起，为什么呢？往后看</span></span><br><span class="line"><span class="comment">      </span></span><br><span class="line"><span class="comment">   需要跳回到前面这个方法</span></span><br><span class="line"><span class="comment">   if (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span></span><br><span class="line"><span class="comment">                  parkAndCheckInterrupt())</span></span><br><span class="line"><span class="comment">                  interrupted = true;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>B线程</strong></p>
<p><strong>第一次循环</strong></p>
<p>1、B线程的前驱节点是哨兵节点（ws == 0），  所以最终走了else分支，执行了  <code>compareAndSetWaitStatus(pred, ws, Node.SIGNAL)</code>方法。将哨兵节点的<code>compareAndSetWaitStatus</code>值变为了-1</p>
<p>2、返回false，返回到<code>acquireQueued()</code>进行第二次循环【不再赘述】。</p>
<p><strong>第二次循环</strong></p>
<p>1、此时B线程的前驱节点–哨兵节点的ws == -1。那么此方法返回true，准备执行parkAndCheckInterrupt</p>
<h3 id="parkAndCheckInterrupt"><a href="#parkAndCheckInterrupt" class="headerlink" title="parkAndCheckInterrupt()"></a>parkAndCheckInterrupt()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	1、如果shouldParkAfterFailedAcquire(p, node)返回true，那么需要执行parkAndCheckInterrupt():</span></span><br><span class="line"><span class="comment">    这个方法很简单，因为前面返回true，所以需要挂起线程，这个方法就是负责挂起线程的，</span></span><br><span class="line"><span class="comment">    2、这里用了LockSupport.park(this)来挂起线程，然后就停在这里了，等待被唤醒=======</span></span><br><span class="line"><span class="comment">	*/</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">parkAndCheckInterrupt</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        LockSupport.park(<span class="keyword">this</span>);</span><br><span class="line">        <span class="keyword">return</span> Thread.interrupted();</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	1、接下来说说如果shouldParkAfterFailedAcquire(p, node)返回false的情况</span></span><br><span class="line"><span class="comment">    2、仔细看shouldParkAfterFailedAcquire(p, node)，我们可以发现，其实第一次进来的时候，一般都不会</span></span><br><span class="line"><span class="comment">    返回true的，原因很简单，前驱节点的waitStatus=-1是依赖于后继节点设置的。也就是说，我都还没给前驱</span></span><br><span class="line"><span class="comment">    设置-1呢，怎么可能是true呢，但是要看到，这个方法是套在循环里的，所以第二次进来的时候状态就是-1了。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">    3、解释下为什么shouldParkAfterFailedAcquire(p, node)返回false的时候不直接挂起线程：</span></span><br><span class="line"><span class="comment">    主要是为了应对在经过这个方法后，node已经是head的直接后继节点了。</span></span><br><span class="line"><span class="comment">    4、假设返回fasle的时候，node已经是head的直接后继节点了，但是你直接挂起了线程，就要走别人唤醒你的那</span></span><br><span class="line"><span class="comment">    几步代码。那这里完全可以重新走一遍for循环，直接尝试下获取锁，可能会更快。注意是可能，不代表一定，因为</span></span><br><span class="line"><span class="comment">    你也无法确定unparkSuccessor释放锁，通知后继节点这个方法执行的快慢。但是你多尝试一次获取锁，总归是快的。</span></span><br><span class="line"><span class="comment">    	for (;;) &#123;</span></span><br><span class="line"><span class="comment">            final Node p = node.predecessor();</span></span><br><span class="line"><span class="comment">            if (p == head &amp;&amp; tryAcquire(arg)) &#123;</span></span><br><span class="line"><span class="comment">                setHead(node);</span></span><br><span class="line"><span class="comment">                p.next = null; // help GC</span></span><br><span class="line"><span class="comment">                failed = false;</span></span><br><span class="line"><span class="comment">                return;</span></span><br><span class="line"><span class="comment">            &#125;</span></span><br><span class="line"><span class="comment">	*/</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>到这一步，B线程才算真正的入队坐稳了。B线程在这里阻塞，或者说挂起。</p>
<h2 id="非公平锁lock-解锁"><a href="#非公平锁lock-解锁" class="headerlink" title="非公平锁lock()解锁"></a>非公平锁lock()解锁</h2><p>然后，就是还需要介绍下唤醒的动作了。我们知道，正常情况下，如果线程没获取到锁，线程会被 <code>LockSupport.park(this);</code> 挂起停止，等待被唤醒。</p>
<h3 id="release-和tryRelease"><a href="#release-和tryRelease" class="headerlink" title="release()和tryRelease()"></a>release()和tryRelease()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 唤醒的代码还是比较简单的，你如果上面加锁的都看懂了，下面都不需要看就知道怎么回事了</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.release(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//AQS类的方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">release</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="comment">//h是哨兵节点</span></span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">            unparkSuccessor(h);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 回到ReentrantLock看tryRelease方法</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryRelease</span><span class="params">(<span class="keyword">int</span> releases)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> c = getState() - releases;</span><br><span class="line">    <span class="keyword">if</span> (Thread.currentThread() != getExclusiveOwnerThread())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalMonitorStateException();</span><br><span class="line">    <span class="comment">// 是否完全释放锁</span></span><br><span class="line">    <span class="keyword">boolean</span> free = <span class="keyword">false</span>;</span><br><span class="line">    <span class="comment">// 其实就是重入的问题，如果c==0，也就是说没有嵌套锁了，可以释放了，否则还不能释放掉</span></span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">        free = <span class="keyword">true</span>;</span><br><span class="line">        setExclusiveOwnerThread(<span class="keyword">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    setState(c);</span><br><span class="line">    <span class="keyword">return</span> free;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="unparkSuccessor"><a href="#unparkSuccessor" class="headerlink" title="unparkSuccessor()"></a>unparkSuccessor()</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 唤醒后继节点，从上面调用处知道，参数node是head头结点（或者说是哨兵节点，因为本身head就指向了哨兵节点）</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">unparkSuccessor</span><span class="params">(Node node)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> ws = node.waitStatus;</span><br><span class="line">    <span class="comment">// 如果head节点当前waitStatus&lt;0, 将其修改为0</span></span><br><span class="line">    <span class="keyword">if</span> (ws &lt; <span class="number">0</span>)</span><br><span class="line">        compareAndSetWaitStatus(node, ws, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    1、下面的代码就是唤醒后继节点，但是有可能后继节点取消了等待（waitStatus==1）从队尾往前找，</span></span><br><span class="line"><span class="comment">    找到waitStatus&lt;=0的所有节点中排在最前面的</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    Node s = node.next;</span><br><span class="line">    <span class="keyword">if</span> (s == <span class="keyword">null</span> || s.waitStatus &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        s = <span class="keyword">null</span>;</span><br><span class="line">        <span class="comment">// 从后往前找，仔细看代码，不必担心中间有节点取消(waitStatus==1)的情况</span></span><br><span class="line">        <span class="keyword">for</span> (Node t = tail; t != <span class="keyword">null</span> &amp;&amp; t != node; t = t.prev)</span><br><span class="line">            <span class="keyword">if</span> (t.waitStatus &lt;= <span class="number">0</span>)</span><br><span class="line">                s = t;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (s != <span class="keyword">null</span>)</span><br><span class="line">        <span class="comment">// 唤醒线程</span></span><br><span class="line">        LockSupport.unpark(s.thread);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>B线程</strong></p>
<p>1、哨兵节点的后一个节点就是B节点，B节点的waitStatus == 0，所以就直接走唤醒线程那一步了。</p>
<h3 id="唤醒之后"><a href="#唤醒之后" class="headerlink" title="唤醒之后"></a>唤醒之后</h3><p>唤醒线程以后，被唤醒的线程将从以下代码中继续往前走：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">parkAndCheckInterrupt</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    LockSupport.park(<span class="keyword">this</span>); <span class="comment">// 刚刚线程被挂起在这里了</span></span><br><span class="line">    <span class="keyword">return</span> Thread.interrupted();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 又回到这个方法了：acquireQueued(final Node node, int arg)，这个时候，node的前驱是head了</span></span><br></pre></td></tr></table></figure>

<p>返回这个方法进行第三次循环</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//node还是B节点</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">acquireQueued</span><span class="params">(<span class="keyword">final</span> Node node, <span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">boolean</span> failed = <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">boolean</span> interrupted = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">final</span> Node p = node.predecessor();</span><br><span class="line">                <span class="comment">//A线程走了，B就可以tryacquire成功</span></span><br><span class="line">                <span class="keyword">if</span> (p == head &amp;&amp; tryAcquire(arg)) &#123;</span><br><span class="line">                    setHead(node);</span><br><span class="line">                    p.next = <span class="keyword">null</span>; <span class="comment">// help GC，</span></span><br><span class="line">                    failed = <span class="keyword">false</span>;</span><br><span class="line">                    <span class="keyword">return</span> interrupted;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                    parkAndCheckInterrupt())</span><br><span class="line">                    interrupted = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (failed)</span><br><span class="line">                cancelAcquire(node);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>1、B线程<code>tryAcquire()</code>成功之后就占有了state，也就是拿到了锁。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">nonfairTryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">final</span> Thread current = Thread.currentThread();</span><br><span class="line">            <span class="keyword">int</span> c = getState();</span><br><span class="line">            <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">                    setExclusiveOwnerThread(current);</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">                <span class="keyword">int</span> nextc = c + acquires;</span><br><span class="line">                <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">                setState(nextc);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">setExclusiveOwnerThread</span><span class="params">(Thread thread)</span> </span>&#123;</span><br><span class="line">        exclusiveOwnerThread = thread;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>2、此时state那里有B线程的引用<code>exclusiveOwnerThread</code>，队列里也有B线程的引用，需要把队列里的多余引用给GC掉。</p>
<p>3、AQS采用的是将head指向B节点成为新的哨兵节点，旧的哨兵节点因为没有任何引用指向了，慢慢就会被GC掉。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Fourth_stage/0010.png">





<h1 id="公平锁和非公平锁"><a href="#公平锁和非公平锁" class="headerlink" title="公平锁和非公平锁"></a>公平锁和非公平锁</h1><p>看了上面的源码，这个知识点应该是可以很轻松理解的。公平锁和非公平锁在源码层次只有几处不一样。</p>
<h2 id="构造"><a href="#构造" class="headerlink" title="构造"></a>构造</h2><p>ReentrantLock 默认采用非公平锁，除非你在构造方法中传入参数 true 。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ReentrantLock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 默认非公平锁</span></span><br><span class="line">    sync = <span class="keyword">new</span> NonfairSync();</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ReentrantLock</span><span class="params">(<span class="keyword">boolean</span> fair)</span> </span>&#123;</span><br><span class="line">    sync = fair ? <span class="keyword">new</span> FairSync() : <span class="keyword">new</span> NonfairSync();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="非公平锁的-lock-方法"><a href="#非公平锁的-lock-方法" class="headerlink" title="非公平锁的 lock 方法"></a>非公平锁的 lock 方法</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">NonfairSync</span> <span class="keyword">extends</span> <span class="title">Sync</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 1、和公平锁相比，这里会直接先进行一次CAS，成功就返回了。这是第一处不一样</span></span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, <span class="number">1</span>))</span><br><span class="line">            setExclusiveOwnerThread(Thread.currentThread());</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            acquire(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// AbstractQueuedSynchronizer类的acquire(int arg)方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">            selfInterrupt();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> nonfairTryAcquire(acquires);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">nonfairTryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> Thread current = Thread.currentThread();</span><br><span class="line">    <span class="keyword">int</span> c = getState();</span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 这里没有对队列进行判断，直接CAS抢，这是第二点不一样【对比请看下方公平锁的lock】</span></span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">            setExclusiveOwnerThread(current);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">        <span class="keyword">int</span> nextc = c + acquires;</span><br><span class="line">        <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">        setState(nextc);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="公平锁的-lock-方法"><a href="#公平锁的-lock-方法" class="headerlink" title="公平锁的 lock 方法"></a>公平锁的 lock 方法</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">FairSync</span> <span class="keyword">extends</span> <span class="title">Sync</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        acquire(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// AbstractQueuedSynchronizer类的acquire(int arg)方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">            selfInterrupt();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> Thread current = Thread.currentThread();</span><br><span class="line">        <span class="keyword">int</span> c = getState();</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 2、和非公平锁相比，这里多了一个判断：是否有线程在队列列等待，有我就不抢了</span></span><br><span class="line">            <span class="keyword">if</span> (!hasQueuedPredecessors() &amp;&amp;</span><br><span class="line">                compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">                setExclusiveOwnerThread(current);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">            <span class="keyword">int</span> nextc = c + acquires;</span><br><span class="line">            <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">            setState(nextc);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="推荐"><a href="#推荐" class="headerlink" title="推荐"></a>推荐</h1><p><a target="_blank" rel="noopener" href="https://coderbee.net/index.php/concurrent/20131115/577">CLH队列</a></p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">youthlql</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://imlql.cn/post/92c4503d.html">https://imlql.cn/post/92c4503d.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://imlql.cn" target="_blank">风祈的时光录</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Java%E5%B9%B6%E5%8F%91/">Java并发</a><a class="post-meta__tags" href="/tags/AQS%E6%BA%90%E7%A0%81/">AQS源码</a></div><div class="post_share"></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" alt="微信"/></a><div class="post-qr-code-desc">微信</div></li><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" alt="支付宝"/></a><div class="post-qr-code-desc">支付宝</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/post/f1f58db2.html"><img class="prev-cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/java.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">HashMap-JDK7源码讲解</div></div></a></div><div class="next-post pull-right"><a href="/post/adb2faf0.html"><img class="next-cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/java.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">万字长文详解Java泛型</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/post/8210870.html" title="Java并发体系-第二阶段-锁与同步-[2]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-07</div><div class="title">Java并发体系-第二阶段-锁与同步-[2]</div></div></a></div><div><a href="/post/230c5bb3.html" title="Java并发体系-第二阶段-锁与同步-[1]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-06</div><div class="title">Java并发体系-第二阶段-锁与同步-[1]</div></div></a></div><div><a href="/post/5be45d9e.html" title="Java并发体系-第三阶段-JUC并发包-[1]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-09</div><div class="title">Java并发体系-第三阶段-JUC并发包-[1]</div></div></a></div><div><a href="/post/113a3931.html" title="Java并发体系-第二阶段-锁与同步-[3]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-08</div><div class="title">Java并发体系-第二阶段-锁与同步-[3]</div></div></a></div><div><a href="/post/efc79183.html" title="Java并发体系-第一阶段-多线程基础知识"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-05</div><div class="title">Java并发体系-第一阶段-多线程基础知识</div></div></a></div><div><a href="/post/70c90e5d.html" title="Java并发体系-第三阶段-JUC并发包-[2]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-10</div><div class="title">Java并发体系-第三阶段-JUC并发包-[2]</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%8F%AF%E9%87%8D%E5%85%A5%E9%94%81"><span class="toc-text">可重入锁</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#LockSupport"><span class="toc-text">LockSupport</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="toc-text">是什么？</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3%E7%A7%8D%E8%AE%A9%E7%BA%BF%E7%A8%8B%E7%AD%89%E5%BE%85%E5%92%8C%E5%94%A4%E9%86%92%E7%9A%84%E6%96%B9%E6%B3%95"><span class="toc-text">3种让线程等待和唤醒的方法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Object%E7%B1%BB%E6%8F%90%E4%BE%9B%E7%9A%84%E7%AD%89%E5%BE%85%E5%94%A4%E9%86%92%E6%9C%BA%E5%88%B6%E7%9A%84%E7%BC%BA%E7%82%B9"><span class="toc-text">Object类提供的等待唤醒机制的缺点</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%AD%A3%E5%B8%B8%E6%83%85%E5%86%B5%E4%B8%8B"><span class="toc-text">正常情况下</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E6%83%85%E5%86%B51"><span class="toc-text">异常情况1</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E6%83%85%E5%86%B52"><span class="toc-text">异常情况2</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Condion%E7%B1%BB%E6%8F%90%E4%BE%9B%E7%9A%84%E7%AD%89%E5%BE%85%E5%94%A4%E9%86%92%E6%9C%BA%E5%88%B6%E7%9A%84%E7%BC%BA%E7%82%B9"><span class="toc-text">Condion类提供的等待唤醒机制的缺点</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#LockSupport%E7%9A%84%E4%BC%98%E7%82%B9"><span class="toc-text">LockSupport的优点</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E6%83%85%E5%86%B51-1"><span class="toc-text">异常情况1</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8%E6%83%85%E5%86%B52-1"><span class="toc-text">异常情况2</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#AQS"><span class="toc-text">AQS</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#AQS%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="toc-text">AQS是什么？</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AQS%E8%83%BD%E5%B9%B2%E5%98%9B%EF%BC%9F"><span class="toc-text">AQS能干嘛？</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#AQS%E7%8B%AC%E5%8D%A0%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BB%A5ReentrantLock-%E6%BA%90%E7%A0%81%E4%B8%BA%E4%BE%8B%EF%BC%89"><span class="toc-text">AQS独占模式（以ReentrantLock 源码为例）</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#AQS%E7%BB%93%E6%9E%84"><span class="toc-text">AQS结构</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Node%E7%B1%BB%E7%BB%93%E6%9E%84"><span class="toc-text">Node类结构</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AQS%E9%98%9F%E5%88%97%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84"><span class="toc-text">AQS队列基本结构</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%80%E5%A7%8B"><span class="toc-text">开始</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81lock-%E5%8A%A0%E9%94%81"><span class="toc-text">非公平锁lock()加锁</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#lock"><span class="toc-text">lock()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#acquire-%E5%92%8CtryAcquire"><span class="toc-text">acquire()和tryAcquire()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#addWaiter"><span class="toc-text">addWaiter()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#enq"><span class="toc-text">enq()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#acquireQueued"><span class="toc-text">acquireQueued()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#shouldParkAfterFailedAcquire"><span class="toc-text">shouldParkAfterFailedAcquire()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#parkAndCheckInterrupt"><span class="toc-text">parkAndCheckInterrupt()</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81lock-%E8%A7%A3%E9%94%81"><span class="toc-text">非公平锁lock()解锁</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#release-%E5%92%8CtryRelease"><span class="toc-text">release()和tryRelease()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#unparkSuccessor"><span class="toc-text">unparkSuccessor()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%94%A4%E9%86%92%E4%B9%8B%E5%90%8E"><span class="toc-text">唤醒之后</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%85%AC%E5%B9%B3%E9%94%81%E5%92%8C%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81"><span class="toc-text">公平锁和非公平锁</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9E%84%E9%80%A0"><span class="toc-text">构造</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81%E7%9A%84-lock-%E6%96%B9%E6%B3%95"><span class="toc-text">非公平锁的 lock 方法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%85%AC%E5%B9%B3%E9%94%81%E7%9A%84-lock-%E6%96%B9%E6%B3%95"><span class="toc-text">公平锁的 lock 方法</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E6%8E%A8%E8%8D%90"><span class="toc-text">推荐</span></a></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By youthlql</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text"><a href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral" target="_blank" rel="noopener" class="one-pan-link-mark"><img style="position:relative;top:-3px; " src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/upyun/logo.png" align="absmiddle" width="60px" height="30px"></a><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn"><img class="icp-icon" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/icp.png"><span>鄂ICP备19028890号</span></a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">简</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">本地搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.umd.js"></script><script src="https://npm.elemecdn.com/instant.page/instantpage.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script defer src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></div></body></html>